package com.zx.sms.connect.manager;

import io.netty.channel.ChannelFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhangzl7 系统连接的统一管理器，负责连接服务端，或者开启监听端口，等客户端连接 。
 * 采取的策略是：服务端 宕机，只清空与之对应 的 map中的EndpointConnector ，其他保留。
 */
public enum EndpointManager implements EndpointManagerInterface {
	INS;
	private static final Logger logger = LoggerFactory.getLogger(EndpointManager.class);

	private Set<EndpointEntity> endpoints = Collections.synchronizedSet(new HashSet<EndpointEntity>());

	private ConcurrentHashMap<String, EndpointEntity> idMap = new ConcurrentHashMap<String, EndpointEntity>();

	private ConcurrentHashMap<String, EndpointConnector<?>> map = new ConcurrentHashMap<String, EndpointConnector<?>>();


	public synchronized void addEndpointEntity(EndpointEntity entity) {
		endpoints.add(entity);
		idMap.put(entity.getId(), entity);
		if (entity instanceof ServerEndpoint) {
			ServerEndpoint serverentity = (ServerEndpoint) entity;
			if (serverentity.getAllChild() != null)
				for (EndpointEntity child : serverentity.getAllChild()) {
					endpoints.add(child);
					idMap.put(child.getId(), child);
				}
		}
	}

	@Override
	public synchronized void remove(String id) {
		EndpointEntity entity = idMap.remove(id);
		if (entity != null) {
			endpoints.remove(entity);
			close(entity);
		}
	}


	public synchronized void close(EndpointEntity entity) {
		EndpointConnector<?> conn = map.get(entity.getId());
		if (conn != null) {
			try {
				conn.close();
				// 关闭所有连接，并把Connector删掉
				map.remove(entity.getId());

			} catch (Exception e) {
				logger.error("close Error", e);
			}
		}
	}

	public EndpointEntity initEndpoint(EndpointEntity entity) {
		EndpointConnector connect = map.get(entity.getId());
		if (connect != null) {
			return idMap.get(entity.getId());
		}

		synchronized (INS){
			connect = map.get(entity.getId());
			if (connect != null) {
				return idMap.get(entity.getId());
			}
			return doOpenEndpoint(entity);
		}
	}


	public synchronized void openEndpoint(EndpointEntity entity) {
		doOpenEndpoint(entity);
	}
	public EndpointEntity doOpenEndpoint(EndpointEntity entity){
		if (!entity.isValid()){
			return entity;
		}

		EndpointEntity old = idMap.get(entity.getId());
		if (old == null) {
			addEndpointEntity(entity);
			old = entity;
		}

		EndpointConnector<?> conn = map.get(entity.getId());
		if (conn == null) {
			conn = entity.buildConnector();
			map.put(entity.getId(), conn);// zhangzhiliang 之所以要先 put。连接失败在remove。是因为可能已经收到了ConnectResponse,但是 future 还未完成 是否成功的判断，导致  map中取不到connect
			try {
				ChannelFuture connFuture = conn.open();
				if (null != connFuture){
					connFuture.get();
					logger.info("open connector，result={}，entity={}",connFuture.isSuccess(),entity);
					Throwable throwable = connFuture.cause();
					if (null != throwable){
						logger.error("open connector，entity={}",entity,throwable);
					}
					if (!connFuture.isSuccess()){
						map.remove(entity.getId());
					}
				}
			} catch (Exception e) {
				logger.error("Open Endpoint Error. {}", entity, e);
				map.remove(entity.getId());
			}
		}
		return old;
	}

	@Deprecated
	public EndpointEntity getEndpointEntity(String id) {
		return idMap.get(id);
	}


	public EndpointConnector<?> getEndpointConnector(EndpointEntity entity) {
		return map.get(entity.getId());
	}

	public EndpointConnector<?> getEndpointConnector(String entityId) {
		return map.get(entityId);
	}


	public void openAll() throws Exception {
		for (EndpointEntity e : endpoints)
			openEndpoint(e);
	}


	public void addAllEndpointEntity(List<EndpointEntity> entities) {
		if (entities == null || entities.size() == 0)
			return;
		for (EndpointEntity entity : entities) {
			if (entity.isValid())
				addEndpointEntity(entity);
		}
	}

	public Set<EndpointEntity> allEndPointEntity() {
		return endpoints;
	}


	public void close() {
		for (EndpointEntity en : endpoints) {
			close(en);
		}
	}

}
